#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <linux/types.h>
#include <linux/videodev2.h>
#include <malloc.h>
#include <math.h>
#include <string.h>
#include <sys/mman.h>
#include <errno.h>
#include <assert.h>
#include <sys/time.h>
#include <linux/fb.h>
#include <time.h>
#include <semaphore.h>

#include <ctime>
#include <pthread.h>
#include <vector>

#include <iostream>
#include <fstream>
#include <string>

#include "ImageReaderSource.h"
#include <zxing/common/Counted.h>
#include <zxing/Binarizer.h>
#include <zxing/MultiFormatReader.h>
#include <zxing/Result.h>
#include <zxing/ReaderException.h>
#include <zxing/common/GlobalHistogramBinarizer.h>
#include <zxing/common/HybridBinarizer.h>
#include <exception>
#include <zxing/Exception.h>
#include <zxing/common/IllegalArgumentException.h>
#include <zxing/BinaryBitmap.h>
#include <zxing/DecodeHints.h>
#include <zxing/Binarizer.h>
#include <zxing/LuminanceSource.h>
#include <zxing/MultiFormatReader.h>
#include <zxing/oned/OneDReader.h>
#include <zxing/oned/EAN8Reader.h>
#include <zxing/oned/EAN13Reader.h>
#include <zxing/oned/Code128Reader.h>
#include <zxing/datamatrix/DataMatrixReader.h>
#include <zxing/qrcode/QRCodeReader.h>
#include <zxing/aztec/AztecReader.h>
#include <zxing/common/GlobalHistogramBinarizer.h>
#include <zxing/Exception.h>


#include <zxing/qrcode/QRCodeReader.h>
#include <zxing/multi/qrcode/QRCodeMultiReader.h>
#include <zxing/multi/ByQuadrantReader.h>
#include <zxing/multi/MultipleBarcodeReader.h>
#include <zxing/multi/GenericMultipleBarcodeReader.h>

//#define OPENCV_DECODE
#ifdef OPENCV_DECODE   //OPENCV_DECODE
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <cctype>
#include <opencv/cv.h>
#endif

using namespace std;
using namespace zxing;
using namespace zxing::multi;
using namespace zxing::qrcode;


#include "fb_device.h"
#include "jpenenc.h"
#include "v4l_cpp.h"
#include "dsc_cpp.h"

#ifdef JPEG_LIB   //如果使用jpeglib库需要包含jpeglib.h
extern "C"{
	#include "/work/jpeg_arm_lib/include/jpeglib.h"
};
#endif


fb_device fb_dev;
camera_v4l v4l_dev;
jpeg_device jpeg_dev;
dsc dsc_dev;

int s_i32FBWidth;
int s_i32FBHeight;


int32_t g_i32FBFd;
int32_t g_i32KpdFd;

uint8_t *g_pu8FBBufAddr;
uint32_t u32FBBufSize;


//#define DECODE_RGB  //使用RGB数据进行解码
#define DECODE_YUV    //使用YUV的数据进行解码


unsigned char decode_press_key_flag = 0;   //解码键按下的标志
static pthread_t capture_tid;              //线程id

//------------------------------------------------------------------------//

#ifdef DECODE_YUV   //使用YUV数据解码
unsigned char yuv_buf[640*480] = {0};
#endif //DECODE_YUV

//----------------------------------------------------------------------------//

#ifdef DECODE_RGB   //使用RGB数据解码
unsigned char bmp_buf[640*480*4] = {0};     //yuv422的数据处理后存入到bum_buf缓冲区
unsigned char fb_buf [640*480*2] = {0};     //yuv422数据缓冲区
unsigned char gray_buf[640*480] = {0};     //灰度图像缓冲区-使用RGB数据解码时使用
#endif //DECODE_RGB

//----------------------------------------------------------------------------//
#ifdef DECODE_RGB   //使用RGB数据解码
inline int clip(int value, int min, int max)
{
    return (value > max ? max : value < min ? min : value);
}
//----------------------------------------------------------------------------//
//将采集好的数据放到文件中
//yuv422 plane 格式，数据分为三部分Y U V
//Y 640 * 480
//U 640 * 480 / 2
//V 640 * 480 / 2
int process_image(void *addr,int width,int height)
{
    unsigned char* in=(unsigned char*)addr;

    int image_size = width * height;
    int y0,u,y1,v,r,g,b;
    long location=0;

    int y_base_addr = 0;
    int u_base_addr = width * height;
    int v_base_addr = u_base_addr + (width * height / 2);

	for (y_base_addr = 0; y_base_addr < image_size; y_base_addr += 2)
	{
		//location = (x) * (sVar.bits_per_pixel/8) + (y) * finfo.line_length;
		y0 = in[y_base_addr];
		u  = in[u_base_addr] - 128;
		y1 = in[y_base_addr  + 1];
		v  = in[v_base_addr] - 128;

		r = (298 * y0 + 409 * v + 128) >> 8;
		g = (298 * y0 - 100 * u - 208 * v + 128) >> 8;
		b = (298 * y0 + 516 * u + 128) >> 8;

		bmp_buf[ location + 0] = clip(b, 0, 255);
		bmp_buf[ location + 1] = clip(g, 0, 255);
		bmp_buf[ location + 2] = clip(r, 0, 255);
		bmp_buf[ location + 3] = 255;

		r = (298 * y1 + 409 * v + 128) >> 8;
		g = (298 * y1 - 100 * u - 208 * v + 128) >> 8;
		b = (298 * y1 + 516 * u + 128) >> 8;

		bmp_buf[ location + 4] = clip(b, 0, 255);
		bmp_buf[ location + 5] = clip(g, 0, 255);
		bmp_buf[ location + 6] = clip(r, 0, 255);
		bmp_buf[ location + 7] = 255;

		location = location + 8;
		u_base_addr = u_base_addr + 1;
		v_base_addr = v_base_addr + 1;
	}

    return 0;
}
//----------------------------------------------------------------------------//
//RGB图像 转 灰度图像 
void rgb_2_gray(void *in_image,int width,int height)
{
	unsigned char *rgb_image = (unsigned char *)in_image;
	int x,y;
	unsigned int size = 0;
	for (x = 0;x < height; ++x)  //480
	{
		for (y = 0; y < width;++y) //640
		{
			gray_buf[size] = (rgb_image[0] + rgb_image[1] + rgb_image[2]) / 3;
			rgb_image = rgb_image + 4;
			size++;
		}
	}
}

//----------------------------------------------------------------------------//
/*
使用RGB数据进行解码，得到的RGB数据需要转换为灰度图像。
*/

class RGBBitmapSource : public LuminanceSource
{
private:
	unsigned char * rgb_image;
	unsigned int rgb_image_width;
	unsigned int rgb_image_height;
	unsigned int left;
	unsigned int top;
public:
	 RGBBitmapSource(int width_,int height_,unsigned char *data) : LuminanceSource(width_, height_)
	 {
	 	rgb_image_width = width_;
	 	rgb_image_height = height_;

	 	rgb_image = new unsigned char[width_ * height_];

	 	for(unsigned int y = 0; y < height_ * width_; y++)
	 	{
	 		rgb_image[y] = data[y];
	 	}
	 }
	~ RGBBitmapSource()
	{
		//delete[] rgb_image;
	}

public:
	int getWidth() const { return rgb_image_width; }
	int getHeight() const { return rgb_image_height; }

	ArrayRef<char>  getRow(int y, ArrayRef<char>  row) const
	{
		int width_ = getWidth();
		if(!row)
		{
			row = ArrayRef<char>(width_);
		}

		const unsigned char *p = (rgb_image + y * width_);
		for (int i = 0; i < width_; ++i,++p)
		{
			row[i] = *p;
		}

		return row;
	}
	ArrayRef<char>  getMatrix()const
	{
		int width_ = getWidth();
		int height_ = getHeight();
		ArrayRef<char> matrix = ArrayRef<char>(width_ * height_);

		for (int y = 0; y < height_; ++y)
		{
			const unsigned char *p = (rgb_image + y * width_);
			for (int x = 0; x < width_; ++x,++p)
			{
				matrix[y * width_ + x] = *p;
			}
		}
		return matrix;
	}
};

//----------------------------------------------------------------------------//

class RGBImageBitmapSource : public LuminanceSource
{
private:
	typedef LuminanceSource Super;
	unsigned int rgb_image_width;
	unsigned int rgb_image_height;
	const zxing::ArrayRef<char> image_rgb;

public:
	 RGBImageBitmapSource(zxing::ArrayRef<char> image,int width,int height);
	~ RGBImageBitmapSource(){};

public:
	static zxing::Ref<LuminanceSource> create(char *image_data,int width,int height);


	int getWidth() const { return rgb_image_width; }
	int getHeight() const { return rgb_image_height; }

	ArrayRef<char>  getRow(int y, ArrayRef<char>  row) const;
	ArrayRef<char>  getMatrix() const;
};

//----------------------------------------------------------------------------//
//创建一个RGBImageBitmapSource 对像
Ref<LuminanceSource> RGBImageBitmapSource::create(char *image_data,int width,int height)
{
  zxing::ArrayRef<char> image;
  char *buffer = image_data;
  image = zxing::ArrayRef<char>(buffer, width * height);
  return Ref<LuminanceSource>(new RGBImageBitmapSource(image, width, height));
}

//----------------------------------------------------------------------------//

RGBImageBitmapSource::RGBImageBitmapSource(ArrayRef<char> image_, int width, int height)
    : Super(width, height), image_rgb(image_)
{
    	rgb_image_width = width;
		rgb_image_height = height;
}

//----------------------------------------------------------------------------//

ArrayRef<char>  RGBImageBitmapSource::getRow(int y, ArrayRef<char>  row) const
{
	int width_ = getWidth();
	if(!row)
	{
		row = ArrayRef<char>(width_);
	}
	const char *p = &image_rgb[0] + y * width_;
	for(int i = 0; i < width_; ++i,++p)
	{
		row[i] = *p;
	}
	return row;
}

//----------------------------------------------------------------------------//

ArrayRef<char>  RGBImageBitmapSource::getMatrix() const
{


	int width_ = getWidth();
	int height_ = getHeight();
	zxing::ArrayRef<char> matrix(width_ * height_);

	const char *p = &image_rgb[0];
	char *m = &matrix[0];
	for(int y = 0; y < height_; ++y)
	{
		for (int x = 0; x < width_; ++x)
		{
			*m = *p;
			p++;
			m++;
		}
	}
	return matrix;
}
#endif //DECODE_RGB
//----------------------------------------------------------------------------//

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//

//----------------------------------------------------------------------------//

#ifdef DECODE_YUV
/*
直接使用摄像头输出的数据进行解码。
*/
class PlanarYUVLuminanceSource : public LuminanceSource
{
private:
	typedef LuminanceSource Super;
	unsigned int yuv_image_width;
	unsigned int yuv_image_height;
	const zxing::ArrayRef<char> image_yuv;


public:
	 PlanarYUVLuminanceSource(zxing::ArrayRef<char> image,int width,int height);
	~ PlanarYUVLuminanceSource(){};

public:
	static zxing::Ref<LuminanceSource> create(char *image_data,int width,int height);


	int getWidth() const { return yuv_image_width; }
	int getHeight() const { return yuv_image_height; }

	ArrayRef<char>  getRow(int y, ArrayRef<char>  row) const;
	ArrayRef<char>  getMatrix() const;
};

//----------------------------------------------------------------------------//

PlanarYUVLuminanceSource::PlanarYUVLuminanceSource(ArrayRef<char> image_, int width, int height)
    : Super(width, height), image_yuv(image_)
{
	yuv_image_width = width;
	yuv_image_height = height;
}

//----------------------------------------------------------------------------//

Ref<LuminanceSource> PlanarYUVLuminanceSource::create(char *image_data,int width,int height)
{
	zxing::ArrayRef<char> image;
	char *buffer = image_data;
	image = zxing::ArrayRef<char>(buffer, width * height);
	return Ref<LuminanceSource>(new PlanarYUVLuminanceSource(image, width, height));
}

//----------------------------------------------------------------------------//

ArrayRef<char>  PlanarYUVLuminanceSource::getRow(int y, ArrayRef<char>  row) const
{
	int width_ = getWidth();
	if(!row)
	{
		row = ArrayRef<char>(width_);
	}
	const char *p = &image_yuv[0] + y * width_;
	for(int i= 0; i < width_; ++i,++p)
	{
		row[i] = *p;
	}
	return row;
}

//----------------------------------------------------------------------------//

ArrayRef<char>  PlanarYUVLuminanceSource::getMatrix() const
{
	int width_ = getWidth();
	int height_ = getHeight();

	zxing::ArrayRef<char>matrix(width_ * height_);

	const char *p = &image_yuv[0];

	char *m = &matrix[0];
	for(int y = 0; y < height_; ++y)
	{
		for(int x = 0; x < width_; ++x)
		{
			*m = *p;
			p++;
			m++;
		}
	}
	return matrix;
}
#endif  //DECODE_YUV

//----------------------------------------------------------------------------//

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//

//----------------------------------------------------------------------------//

#ifdef OPENCV_DECODE   //OPENCV_DECODE
class OpenCVBitmapSource : public LuminanceSource
{
private:
	IplImage *m_pImage;
public:
	OpenCVBitmapSource(IplImage *image): LuminanceSource(image->width,image->height)
	{
		m_pImage = image;
		printf("width = %d height = %d\n", getWidth(),getHeight() );
		printf("width step = %d\n",m_pImage->widthStep );
		printf("channel = %d \n",m_pImage->nChannels );
		printf("depth = %d \n",m_pImage->depth );
	}

	~OpenCVBitmapSource()
	{
		// when supporting cropping or rotation, the call to cvReleaseImage()
		// leads to crashes. obviously m_pImage must be managed ref-counted.
		// cvReleaseImage(&m_pImage);
	}

	int getWidth() const { return m_pImage->width; }
	int getHeight() const { return m_pImage->height; }

	ArrayRef<char>  getRow(int y, ArrayRef<char>  row) const
	{
		int width_ = getWidth();
		if (!row)
			row = ArrayRef<char>(width_);
		const char *p = m_pImage->imageData + y*m_pImage->widthStep;
		//printf("m_pImage->imageData + y*m_pImage->widthStep = %d",m_pImage->widthStep);
		for(int i = 0; i < width_; ++i,++p)
		{
			row[i] = *p;
		}
		return row;
	}

	ArrayRef<char>  getMatrix()const
	{
		int width_ = getWidth();
		int height_ =  getHeight();
		ArrayRef<char>  matrix = ArrayRef<char>(width_ * height_);

		for(int y = 0; y < height_; ++y)
		{
			const char *p = m_pImage->imageData+y*m_pImage->widthStep;
			for(int x = 0; x < width_; ++x,++p)
			{
				matrix[y*width_ +x] = *p;
			}
		}
		return matrix;
	}

	/*
	// The following methods are not supported by this demo (the DataMatrix Reader doesn't call these methods)
	bool isCropSupported() const { return false; }
	Ref<LuminanceSource> crop(int left, int top, int width, int height) {}
	bool isRotateSupported() const { return false; }
	Ref<LuminanceSource> rotateCounterClockwise() {}
	*/
};
#endif   //OPENCV

//----------------------------------------------------------------------------//

int image_decode(Reader *reader,Ref<LuminanceSource> source)
{
	int ret = -1;
	try
	{
		Ref<Binarizer> binarizer (new HybridBinarizer(source));//(new GlobalHistogramBinarizer(source));
		Ref<BinaryBitmap> bitmap(new BinaryBitmap(binarizer));
		Ref<Result> result(reader->decode(bitmap, DecodeHints(DecodeHints::DEFAULT_HINT)));
		cout << result->getText()->getText() << endl;
		ret = 0;
	}
	catch (zxing::Exception& e)
	{
		ret = -1;
		//cerr << "Error: " << e.what() << endl;
	}
	return ret;
}

//----------------------------------------------------------------------------//

int InitKpdDevice(void)
{

	char achDevName[256];
	char achKeypad[] = "Keypad";
	char achKpdDevice[] = "/dev/input/event0";
	int32_t i32KpdFd= -1;
	uint32_t u32Result;
	int32_t i;

	u32Result = 0;
	for (i = 0; i < RETRY_NUM; i++) {
		printf("trying to open %s ...\n", achKpdDevice);
		if ((i32KpdFd=open(achKpdDevice, O_RDONLY | O_NONBLOCK)) < 0) {
			break;
		}
		memset(achDevName, 0x0, sizeof(achDevName));
		if (ioctl(i32KpdFd, EVIOCGNAME(sizeof(achDevName)), achDevName) < 0) {
			printf("%s evdev ioctl error!\n", achKpdDevice);
		}
		else{
			if (strstr(achDevName, achKeypad) != NULL) {
				printf("Keypad device is %s\n", achKpdDevice);
				u32Result = 1;
				break;
			}
		}
		close(i32KpdFd);
		achKpdDevice[16]++;
	}

	if (u32Result == 0) {
		printf("can not find any Keypad device!\n");
		return -1;
	}

	return i32KpdFd;
}

//----------------------------------------------------------------------------//

void * capture_thread(void *pVoid)
{
	pVoid = pVoid;
	struct input_event sKpdData;
	int ret = -1;

	while(1)
	{
		ret = read(g_i32KpdFd,&sKpdData,sizeof(sKpdData));

		if((sKpdData.code == CAMERA) && (sKpdData.value == 0))//up
		{
			decode_press_key_flag = 0;
			printf("%s\n","up" );
		}
		if((sKpdData.code == CAMERA) && (sKpdData.value == 1))//down
		{
			decode_press_key_flag = 1;
			printf("%s\n","down" );
		}

	}
    return (void *)0;
}

//----------------------------------------------------------------------------//

int main()
{

	E_IMAGE_RESOL ePreviewImgResol = eIMAGE_QVGA;
	E_IMAGE_RESOL eEncodeImgResol = eIMAGE_VGA;
	int32_t i32SensorId;

	int32_t i32Ret = 0;

	MultiFormatReader reader;
	Ref<LuminanceSource> source ;

	int32_t i32KeyCode;
	uint8_t *pu8PicPtr;
	uint64_t u64TS;
	uint32_t u32PicPhyAdr;
	uint32_t u32EncodeImgWidth=0, u32EncodeImgHeight=0;
	uint32_t u32PreviewImgWidth=0, u32PreviewImgHeight=0;


	pthread_create(&capture_tid,NULL,capture_thread,NULL);


	u32PreviewImgWidth  = 320*2;
	u32PreviewImgHeight = 240*2;
	u32EncodeImgWidth  = 640;
	u32EncodeImgHeight = 480;

	usleep(1000);
	printf("Set preview image resolution %d \n", ePreviewImgResol);
	printf("Set encode image resolution %d \n", eEncodeImgResol);



	g_i32KpdFd = InitKpdDevice();   //按键初始化
	if(g_i32KpdFd < 0)
	{
		i32Ret = -1;
		return -1;
	}

	g_i32FBFd = fb_dev.InitFBDevice(&g_pu8FBBufAddr, &u32FBBufSize);   //初始化FB设备
	if(g_i32FBFd < 0){
		close(g_i32KpdFd);
		i32Ret = -1;
		printf("Init fb device fail\n");
		goto exit_prog;
	}
	s_i32FBWidth = fb_dev.fb_get_width();
	s_i32FBHeight = fb_dev.fb_get_height();
	printf("u32FBBufSize = %d\n ",u32FBBufSize);


	if(v4l_dev.InitV4LDevice(eEncodeImgResol) != ERR_V4L_SUCCESS)  //初始化v4l设备
	{
		ioctl(g_i32FBFd, IOCTL_LCD_DISABLE_INT, 0);
		ioctl(g_i32FBFd, VIDEO_FORMAT_CHANGE, DISPLAY_MODE_RGB565); //2012-0808
		ioctl(g_i32FBFd, IOCTL_LCD_ENABLE_INT, 0);
		munmap(g_pu8FBBufAddr, u32FBBufSize);
		close(g_i32FBFd);
		i32Ret = -1;
		printf("Init V4L device fail\n");
		goto exit_prog;
	}

	ioctl(g_i32FBFd, IOCTL_LCD_ENABLE_INT, 0);


	//Planar YUV422
	v4l_dev.SetV4LEncode(0,u32EncodeImgWidth,u32EncodeImgHeight,VIDEO_PALETTE_YUV422P);

	v4l_dev.SetV4LViewWindow(s_i32FBWidth, s_i32FBHeight, u32PreviewImgWidth, u32PreviewImgHeight);
	printf("Ready to start capture\n");
	v4l_dev.StartV4LCapture();
	printf("Start capturing\n");

	ioctl(g_i32FBFd, IOCTL_LCD_ENABLE_INT, 0);

	printf("Ready to query user control\n");
	v4l_dev.QueryV4LUserControl();

	v4l_dev.QueryV4LSensorID(&i32SensorId);
	printf("Sensor ID = %d\n", i32SensorId);
	printf("Ready to start preview\n");
	v4l_dev.StartPreview();
	while(1)
	{
		if(v4l_dev.ReadV4LPicture(&pu8PicPtr, &u64TS, &u32PicPhyAdr) == ERR_V4L_SUCCESS)
		{
			v4l_dev.TriggerV4LNextFrame();
		}

		if(decode_press_key_flag)
		{

#ifdef DECODE_RGB
			//decode
			memcpy(fb_buf,pu8PicPtr,640*480*2);
			process_image(fb_buf,640,480);
			rgb_2_gray(bmp_buf,640,480);

			source = RGBImageBitmapSource::create((char *)gray_buf,640,480);
#else
			memcpy(yuv_buf,pu8PicPtr,640*480);
			source = PlanarYUVLuminanceSource::create((char *)yuv_buf,640,480);

#endif
			if(image_decode(&reader,source) == 0)
			{
				decode_press_key_flag = 0;
			}
		}
	}

	ioctl(g_i32FBFd, VIDEO_FORMAT_CHANGE, DISPLAY_MODE_RGB565);
	close(g_i32FBFd);
	close(g_i32KpdFd);

	return 0;
exit_prog:
	return i32Ret;

}
